import { spawnSync, SpawnSyncReturns } from 'child_process';
import fs from 'fs';
import path from 'path';
import mkdirp from 'mkdirp';
import rimraf from 'rimraf';

export const yarnCommand = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';

export type ExecOutput = SpawnSyncReturns<string> & {
  stdout: string;
  stderr: string;
};

export function run(cmd: string, cwd?: string): ExecOutput {
  const args = cmd.split(/\s/).slice(1);
  const spawnOptions = { cwd };
  const result = spawnSync(cmd.split(/\s/)[0], args, spawnOptions);

  if (result.status !== null && result.status !== 0) {
    const message = `
      ORIGINAL CMD: ${cmd}
      STDOUT: ${result.stdout && result.stdout.toString()}
      STDERR: ${result.stderr && result.stderr.toString()}
      STATUS: ${result.status}
      ERROR: ${result.error && result.error.toString()}
    `;
    throw new Error(message);
  }

  return {
    ...result,
    stdout: result.stdout && result.stdout.toString(),
    stderr: result.stderr && result.stderr.toString(),
  };
}

export function installDeps(projectDir: string) {
  run(`${yarnCommand} --mutex network`, projectDir);
}

export function fileExists(filePath: string) {
  try {
    fs.accessSync(filePath, ((fs as unknown) as { F_OK: number }).F_OK);
    return true;
  } catch (e) {
    return false;
  }
}

export const cleanup = (directory: string) => rimraf.sync(directory);

/**
 * Creates a nested directory with files and their contents
 * writeFiles(
 *   '/home/tmp',
 *   {
 *     'package.json': '{}',
 *     '__tests__/test.test.js': 'test("lol")',
 *   }
 * );
 */
export function writeFiles(
  directory: string,
  files: { [filename: string]: string }
) {
  mkdirp.sync(directory);
  Object.keys(files).forEach(fileOrPath => {
    const filePath = fileOrPath.split(path.sep); // ['tmp', 'a.js']
    const filename = filePath.pop(); // filepath becomes dirPath (no filename)

    if (filePath.length) {
      mkdirp.sync(path.join(...[directory, ...filePath]));
    }
    fs.writeFileSync(
      path.resolve(...[directory, filename || '', ...filePath]),
      files[fileOrPath]
    );
  });
}

export function copyDir(src: string, dest: string) {
  const srcStat = fs.lstatSync(src);
  if (srcStat.isDirectory()) {
    if (!fs.existsSync(dest)) {
      fs.mkdirSync(dest);
    }
    fs.readdirSync(src).map(filePath => {
      return copyDir(path.join(src, filePath), path.join(dest, filePath));
    });
  } else {
    fs.writeFileSync(dest, fs.readFileSync(src));
  }
}

const DEFAULT_PACKAGE_JSON = {
  description: 'THIS IS AN AUTOGENERATED FILE AND SHOULD NOT BE ADDED TO GIT',
};

export function createEmptyPackage(
  directory: string,
  packageJson: Object = DEFAULT_PACKAGE_JSON
) {
  mkdirp.sync(directory);

  fs.writeFileSync(
    path.resolve(directory, 'package.json'),
    JSON.stringify(packageJson, null, 2)
  );
}

export function replaceTestPath(string: string) {
  return string.replace(/^\W+(.*)integration_tests/gm, '<<REPLACED>>');
}
